/*
 * @Author: ReinerLau lk850593913@gmail.com
 * @Date: 2022-09-13 08:45:47
 * @LastEditors: ReinerLau lk850593913@gmail.com
 * @LastEditTime: 2023-02-22 11:22:50
 * @FilePath: \robot\src\views\patroling\composables\useGamepad.js
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { ElNotification } from "element-plus";
import { ref, computed, onBeforeUnmount, ComputedRef } from "vue";
import { difference } from "lodash";

export default () => {
  // 初始化 --------------------------------------------------------------------------------------

  // 是手柄还是方向盘
  let controler: Gamepad;
  const enum controlTypes {
    WHEEL = "方向盘",
    CONTROLLER = "手柄",
  }
  const controlerType: ComputedRef<string> = computed(() => {
    if (!controler) return "未知";
    return controler.id.includes("Wheel")
      ? controlTypes.WHEEL
      : controlTypes.CONTROLLER;
  });

  /**
   * @description: 初始手柄
   * @return {*}
   */
  function initGamepad() {
    const controlers = getConnectedGamepad();
    if (controlers.length === 0) {
      ElNotification({
        message: "请连接手柄",
        type: "warning",
      });
      return;
    }
    controler = findControler() || controlers[0];
    onGamepadOperation();
  }

  function findControler() {
    const result = getConnectedGamepad().find((item) =>
      item.id.includes("CONTROLLER")
    );
    return result;
  }

  /**
   * @description: 与手柄断开连接
   * @return {*}
   */
  function disconnectGamepad() {
    cancelAnimationFrame(rAF);
    controler = null;
  }

  /**
   * @description: 监听手柄连接/断开
   * @return {*}
   */
  function onGamepad() {
    window.addEventListener("gamepadconnected", initGamepad);
    window.addEventListener("gamepaddisconnected", disconnectGamepad);

    onBeforeUnmount(() => {
      disconnectGamepad();
      window.removeEventListener("gamepadconnected", initGamepad);
      window.removeEventListener("gamepaddisconnected", disconnectGamepad);
    });
  }
  onGamepad();

  /**
   * @description: 获取已连接的控制器
   * @return {*}
   */
  function getConnectedGamepad(): Gamepad[] {
    return navigator.getGamepads().filter((gamepad) => gamepad !== null);
  }
  // 首次加载页面要调用一次激活，否则第一次获取不到
  getConnectedGamepad();

  let rAF = null;
  /**
   * @description: 实时监听手柄操作
   * @return {*}
   */
  function onGamepadOperation() {
    const gamepad = getConnectedGamepad().find(
      (item) => item.id === controler.id
    );
    if (!gamepad) return;
    const axes = gamepad.axes;
    const buttons = gamepad.buttons;
    speed.value = setSpeed(axes);
    direction.value = setDirection(axes);

    pantiltDirection.value = setPantilt(axes);

    handleButtons(buttons);

    rAF = requestAnimationFrame(onGamepadOperation);
  }

  // 挡位 ----------------------------------------------------------------------------

  // true：前进，false：后退
  const gear = ref(true);

  // 速度 ------------------------------------------------------------------------------

  const speed = ref(0);
  /**
   * @description: 设置速度
   * @param {*} axes
   * @return {*}
   */
  function setSpeed(axes: ReadonlyArray<number>): number {
    // 获取原来的速度
    let newSpeed = speed.value;
    let gas = 1;
    if (controlerType.value === controlTypes.CONTROLLER) {
      // 如果控制器是手柄
      gas = parseFloat(axes[1].toFixed(1));
      if (gas <= 0) {
        newSpeed = Math.abs(gas) * 1000;
      }
    } else if (controlerType.value === controlTypes.WHEEL) {
      // 如果控制器是方向盘
      gas = parseFloat(axes[2].toFixed(1));
      newSpeed = parseFloat((Math.abs(gas - 1) / 2).toFixed(1)) * 1000;
    }
    // 如果是倒退档
    if (!gear.value) {
      newSpeed = -newSpeed;
    }
    return newSpeed;
  }

  // 转向 -----------------------------------------------------------------------------------

  const direction = ref(0);
  /**
   * @description: 设置转向
   * @param {*} axes 0: 方向盘
   * @return {*}
   */
  function setDirection(axes: ReadonlyArray<number>): number {
    let newDirection = 0;
    let wheel = 0;
    if (controlerType.value === controlTypes.CONTROLLER) {
      wheel = parseFloat(axes[2].toFixed(1));
      // 如果控制器是手柄
      const upward = parseFloat(axes[5].toFixed(1)) <= 0;
      if (upward) {
        newDirection = wheel * 1000;
      }
    } else if (controlerType.value === controlTypes.WHEEL) {
      wheel = parseFloat(axes[0].toFixed(1));
      // 如果控制器是方向盘
      newDirection = wheel * 1000;
    }
    return newDirection;
  }

  // 发起控制请求  ----------------------------------------------------------

  let readyforClickButtons = [];
  const clickedButton = ref(null);

  function handleButtons(buttons: ReadonlyArray<GamepadButton>) {
    const pressedButtons = buttons
      .filter((button) => button.pressed)
      .map((i) => buttons.findIndex((j) => j === i));
    const diff = difference(readyforClickButtons, pressedButtons);
    readyforClickButtons = pressedButtons;
    diff.forEach((i) => {
      clickedButton.value = i;
    });
  }

  const pantiltDirection = ref("");
  const pantiltDirectionMap = new Map([
    [-1.0, "UP"],
    [0.71, "LEFT"],
    [-0.43, "RIGHT"],
    [0.14, "DOWN"],
  ]);

  function setPantilt(axes: ReadonlyArray<number>): string {
    const axe = Number(axes.slice(-1)[0].toFixed(2));
    return pantiltDirectionMap.get(axe);
  }

  return {
    initGamepad,
    controlerType,
    gear,
    speed,
    direction,
    disconnectGamepad,
    clickedButton,
    pantiltDirection,
  };
};
